/***************************************************************************
    File                 : FunctionCurve.cpp
    Project              : SciDAVis
    --------------------------------------------------------------------
    Copyright            : (C) 2006 by Ion Vasilief, Tilman Benkert
    Email (use @ for *)  : ion_vasilief*yahoo.fr, thzs*gmx.net
    Description          : Function curve class

 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *  This program is free software; you can redistribute it and/or modify   *
 *  it under the terms of the GNU General Public License as published by   *
 *  the Free Software Foundation; either version 2 of the License, or      *
 *  (at your option) any later version.                                    *
 *                                                                         *
 *  This program is distributed in the hope that it will be useful,        *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
 *  GNU General Public License for more details.                           *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the Free Software           *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
 *   Boston, MA  02110-1301  USA                                           *
 *                                                                         *
 ***************************************************************************/
#include "FunctionCurve.h"

#include <QMessageBox>

// FIXME: While FunctionCurve itself supports arbitrary scripting
// interpreters now, fit curves assume muParser in many formulas (using
// "^" as power operator). Thus, simply having FunctionCurve honour the
// application-wide scripting environment would break fit curve plotting.
FunctionCurve::FunctionCurve(ApplicationWindow * parent, QString name):
	PlotCurve(name), scripted(ScriptingLangManager::newEnv("muParser", parent))
{
	d_variable = "x";
	setType(Graph::Function);
}

FunctionCurve::FunctionCurve(ApplicationWindow * parent, const FunctionType& t, QString name):
	PlotCurve(name), scripted(ScriptingLangManager::newEnv("muParser", parent)),
	d_function_type(t)
{
	d_variable = "x";
	setType(Graph::Function);
}

void FunctionCurve::setRange(double from, double to)
{
	d_from = from;
	d_to = to;
}

void FunctionCurve::copy(FunctionCurve* f)
{
	d_function_type = f->functionType();
	d_variable = f->variable();
	d_formulas = f->formulas();
	d_from = f->startRange();
	d_to = f->endRange();
}

QString FunctionCurve::legend() const
{
	QString label = title().text() + ": ";
	if (d_function_type == Normal)
		label += d_formulas[0];
	else if (d_function_type == Parametric)
	{
		label += "X(" + d_variable + ")=" + d_formulas[0];
		label += ", Y(" + d_variable + ")=" + d_formulas[1];
	}
	else if (d_function_type == Polar)
	{
		label += "R(" + d_variable + ")=" + d_formulas[0];
		label += ", Theta(" + d_variable + ")=" + d_formulas[1];
	}
	return label;
}

bool FunctionCurve::loadData(int points)
{
	if (!points)
		points = dataSize();

	double * X = new double[points];
	double * Y = new double[points];
	double step = (d_to - d_from)/(double)(points - 1);

	switch(d_function_type) {
		case Normal:
			{
				Script * script = scriptEnv->newScript(d_formulas[0], 0, title().text());
				QObject::connect(script, SIGNAL(error(const QString&,const QString&,int)),
						this, SLOT(scriptError(const QString&,const QString&,int)));
				int i;
				double x;
				for (i=0, x=d_from; i<points; i++, x+=step) {
					X[i] = x;
					script->setDouble(x, d_variable.toUtf8().constData());
					QVariant result = script->eval();
					if (result.type() != QVariant::Double) {
						delete[] X;
						delete[] Y;
						return false;
					}
					Y[i] = result.toDouble();
				}
				break;
			}
		case Parametric:
		case Polar:
			{
				Script * script_x = scriptEnv->newScript(d_formulas[0], 0, title().text());
				Script * script_y = scriptEnv->newScript(d_formulas[1], 0, title().text());
				int i;
				double par;
				for (i=0, par=d_from; i<points; i++, par+=step) {
					script_x->setDouble(par, d_variable.toUtf8().constData());
					script_y->setDouble(par, d_variable.toUtf8().constData());
					QVariant result_x = script_x->eval();
					QVariant result_y = script_y->eval();
					if (result_x.type() != QVariant::Double || result_y.type() != QVariant::Double) {
						delete[] X;
						delete[] Y;
						return false;
					}
					if (d_function_type == Polar) {
						X[i] = result_x.toDouble()*cos(result_y.toDouble());
						Y[i] = result_x.toDouble()*sin(result_y.toDouble());
					} else {
						X[i] = result_x.toDouble();
						Y[i] = result_y.toDouble();
					}
				}
				break;
			}

	}
	setData(X, Y, points);
	delete[] X;
	delete[] Y;
	return true;
}

void FunctionCurve::scriptError(const QString & message, const QString & scriptName, int lineNumber)
{
	QMessageBox::critical(0, tr("Input function error"),
			QString("%1:%2\n\n%3").arg(scriptName).arg(lineNumber).arg(message));
}
